package com.senlingspace.system.service.impl;

import com.google.common.base.Stopwatch;
import com.alibaba.fastjson2.JSON;

import java.math.BigDecimal;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import com.senlingspace.common.constant.HttpStatus;
import com.senlingspace.common.core.domain.AjaxResult;
import com.senlingspace.common.enums.OrderSource;
import com.senlingspace.common.utils.DateUtils;
import com.senlingspace.common.utils.MoneyUtils;
import com.senlingspace.common.utils.NumberGenerator;
import com.senlingspace.common.utils.StringUtils;
import com.senlingspace.system.domain.*;
import com.senlingspace.system.domain.vo.*;
import com.senlingspace.system.domain.dto.FareSchemeRespDto;
import com.senlingspace.system.event.ActionEvent;
import com.senlingspace.system.mapper.*;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import com.senlingspace.system.service.ISlkjFareSchemeService;
import org.springframework.transaction.annotation.Transactional;

/**
 * 票价方案Service业务层处理
 *
 * @author SenlingSpace
 * @date 2024-07-15
 */
@Slf4j
@Service
@AllArgsConstructor
public class SlkjFareSchemeServiceImpl implements ISlkjFareSchemeService {

    private final SlkjFareSchemeMapper slkjFareSchemeMapper;
    private final SlkjTicketCategoryMapper slkjTicketCategoryMapper;
    private final SlkjTicketLevelMapper slkjTicketLevelMapper;
    private final SlkjFareSchemeDetailMapper slkjFareSchemeDetailMapper;
    private final SlkjFareSchemeShopMapper slkjFareSchemeShopMapper;
    private final ProjectMapper projectMapper;
    private final SlkjShopProjectMapper slkjShopProjectMapper;

    private final ApplicationEventPublisher applicationEventPublisher;


    /**
     * 查询票价方案
     *
     * @param id 票价方案主键
     * @return 票价方案
     */
    @Override
    public AjaxResult selectSlkjFareSchemeById(Long id) {
        SlkjFareScheme fareScheme = slkjFareSchemeMapper.selectSlkjFareSchemeById(id);
        if (Objects.isNull(fareScheme)) {
            return AjaxResult.error("票价方案不存在！");
        }

        final FareSchemeDetailVo result = new FareSchemeDetailVo();
        result.setId(fareScheme.getId());
        result.setSchemeName(fareScheme.getSchemeName());
        result.setSchemeNumber(fareScheme.getSchemeNumber());
        result.setSchemeRemark(fareScheme.getSchemeRemark());

        SlkjFareSchemeDetail query = new SlkjFareSchemeDetail();
        query.setFareSchemeId(id);
        query.setStatus(0);
        List<SlkjFareSchemeDetail> fareSchemeDetails = slkjFareSchemeDetailMapper.selectSlkjFareSchemeDetailList(query);
        if (!CollectionUtils.isEmpty(fareSchemeDetails)) {
            List<Long> ticketLevelIds = new ArrayList<>();
            List<Long> categoryIds = new ArrayList<>();
            for (SlkjFareSchemeDetail detail : fareSchemeDetails) {
                ticketLevelIds.add(detail.getTicketLevelId());
                categoryIds.add(detail.getTicketCategoryId());
            }
            Map<Long, SlkjTicketLevel> ticketLevelMap = slkjTicketLevelMapper.selectSlkjTicketLevelListByIds(ticketLevelIds).stream().collect(Collectors.toMap(SlkjTicketLevel::getId, v -> v));
            Map<Long, SlkjTicketCategory> ticketCategoryMap = slkjTicketCategoryMapper.selectSlkjTicketCategoryByIds(categoryIds).stream().collect(Collectors.toMap(SlkjTicketCategory::getId, v -> v));
            List<FareSchemeTicketLevelCategoryVo> ticketLevelCategorys = new ArrayList<>();
            fareSchemeDetails.stream()
                    .collect(Collectors.groupingBy(SlkjFareSchemeDetail::getTicketLevelId))
                    .forEach((k, v) -> {
                        FareSchemeTicketLevelCategoryVo vo = new FareSchemeTicketLevelCategoryVo();
                        SlkjTicketLevel ticketLevel = ticketLevelMap.get(k);
                        vo.setTicketLevelNumber(ticketLevel.getNumber());
                        vo.setTicketLevelId(k);
                        vo.setTicketLevelName(ticketLevel.getName());
                        List<FareSchemeTicketLevelCategoryVo.TicketLevelCategory> levelCategories = new ArrayList<>();
                        v.forEach(detail -> {
                            SlkjTicketCategory ticketCategory = ticketCategoryMap.get(detail.getTicketCategoryId());
                            FareSchemeTicketLevelCategoryVo.TicketLevelCategory category = new FareSchemeTicketLevelCategoryVo.TicketLevelCategory();
                            category.setTicketCategoryId(ticketCategory.getId());
                            category.setTicketCategoryName(ticketCategory.getName());
                            category.setTicketCategoryNumber(ticketCategory.getNumber());
                            category.setPlatformImageUrl(detail.getPlatformImageUrl());

                            String shopPrice = detail.getShopPrice();
                            category.setShopPrice(shopPrice);
                            if (StringUtils.isNotEmpty(shopPrice) && !"0".equals(shopPrice) && !"#".equals(shopPrice)) {
                                category.setShopPrice(MoneyUtils.fen2Yuan(Long.parseLong(shopPrice)));
                            }

                            String miniProgramPrice = detail.getMiniProgramPrice();
                            category.setMiniProgramPrice(miniProgramPrice);
                            if (StringUtils.isNotEmpty(miniProgramPrice) && !"0".equals(miniProgramPrice) && !"#".equals(miniProgramPrice)) {
                                category.setMiniProgramPrice(MoneyUtils.fen2Yuan(Long.parseLong(miniProgramPrice)));
                            }

                            String douyinPrice = detail.getDouyinPrice();
                            category.setDouyinPrice(douyinPrice);
                            if (StringUtils.isNotEmpty(douyinPrice) && !"0".equals(douyinPrice) && !"#".equals(douyinPrice)) {
                                category.setDouyinPrice(MoneyUtils.fen2Yuan(Long.parseLong(douyinPrice)));
                            }

                            String originalPrice = detail.getOriginalPrice();
                            category.setOriginalPrice(originalPrice);
                            if (StringUtils.isNotEmpty(originalPrice) && !"0".equals(originalPrice) && !"#".equals(originalPrice)) {
                                category.setOriginalPrice(MoneyUtils.fen2Yuan(Long.parseLong(originalPrice)));
                            }
                            category.setFareSchemeDetailId(detail.getId());
                            category.setChecked(detail.getChecked());
                            category.setTicketLevelId(detail.getTicketLevelId());
                            levelCategories.add(category);
                        });
                        ticketLevelCategorys.add(vo);
                        vo.setLevelCategories(levelCategories);
                    });
            result.setTicketLevelCategorys(ticketLevelCategorys);
        }
        return AjaxResult.success(result);
    }

    /**
     * 查询票价方案列表
     *
     * @param slkjFareScheme 票价方案
     * @return 票价方案
     */
    @Override
    public List<FareSchemeRespDto> selectSlkjFareSchemeList(FareSchemeQueryVo slkjFareScheme) {
        List<FareSchemeRespDto> result = slkjFareSchemeMapper.selectSlkjFareSchemeListExt(slkjFareScheme);
        return result;
    }

    /**
     * 新增票价方案
     *
     * @param fareSchemeRequest 票价方案
     * @return 结果
     */
    @Transactional
    @Override
    public AjaxResult insertSlkjFareScheme(FareSchemeRequest fareSchemeRequest, Long userId) {
        String schemeName = fareSchemeRequest.getSchemeName();
        if (StringUtils.isEmpty(schemeName)) {
            return AjaxResult.error("票价方案名称不能为空！");
        }
        int count = slkjFareSchemeMapper.countFareSchemeByName(schemeName, null);
        if (count > 0) {
            return AjaxResult.error("票价方案名称已存在！");
        }

        List<FareSchemeRequest.FareSchemeDetailExt> schemeDetails = fareSchemeRequest.getFareSchemeDetails();
        if (CollectionUtils.isEmpty(schemeDetails)) {
            return AjaxResult.error("票价方案明细不能为空！");
        }
        long counted = schemeDetails.stream().filter(sd -> sd.getChecked() == 1).count();
        if (counted == 0) {
            return AjaxResult.error("票价方案明细至少选择一个！");
        }

        long emptyCount = schemeDetails.stream().filter(sd -> sd.getChecked() == 1 && (StringUtils.isEmpty(sd.getOriginalPrice()) || StringUtils.isEmpty(sd.getShopPrice()) || StringUtils.isEmpty(sd.getDouyinPrice()) || StringUtils.isEmpty(sd.getMiniProgramPrice()))).count();
        if (emptyCount > 0) {
            return AjaxResult.error("价格不能为空！");
        }
        long originalPriceValid = schemeDetails.stream().filter(r -> r.getChecked() == 1 && isOriginalPriceValid(r.getShopPrice(), r.getMiniProgramPrice(), r.getDouyinPrice(), r.getOriginalPrice())).count();
        if (originalPriceValid > 0) {
            return AjaxResult.error("原始价格必须大于实际交易价格！");
        }

        SlkjFareScheme fareScheme = new SlkjFareScheme();
        fareScheme.setSchemeNumber(NumberGenerator.getCharacters());
        fareScheme.setSchemeName(schemeName.trim());
        fareScheme.setSchemeRemark(fareSchemeRequest.getSchemeRemark());
        fareScheme.setStatus(0);
        fareScheme.setCreateBy(userId + "");
        fareScheme.setCreateTime(DateUtils.getNowDate());
        slkjFareSchemeMapper.insertSlkjFareScheme(fareScheme);

        List<SlkjFareSchemeDetail> fareSchemeDetails = new ArrayList<>();
        SlkjFareSchemeDetail fareSchemeDetail;
        for (FareSchemeRequest.FareSchemeDetailExt schemeDetail : schemeDetails) {
            fareSchemeDetail = new SlkjFareSchemeDetail();
            fareSchemeDetail.setFareSchemeId(fareScheme.getId());
            fareSchemeDetail.setTicketLevelId(schemeDetail.getTicketLevelId());
            fareSchemeDetail.setTicketCategoryId(schemeDetail.getTicketCategoryId());
            fareSchemeDetail.setStatus(0);
            fareSchemeDetail.setChecked(schemeDetail.getChecked());

            String originalPrice = schemeDetail.getOriginalPrice();
            fareSchemeDetail.setOriginalPrice(originalPrice);
            if (StringUtils.isNotEmpty(originalPrice) && !"0".equals(originalPrice) && !"#".equals(originalPrice)) {
                fareSchemeDetail.setOriginalPrice(MoneyUtils.yuan2Fen(originalPrice) + "");
            }

            String shopPrice = schemeDetail.getShopPrice();
            fareSchemeDetail.setShopPrice(shopPrice);
            if (StringUtils.isNotEmpty(shopPrice) && !"0".equals(shopPrice) && !"#".equals(shopPrice)) {
                fareSchemeDetail.setShopPrice(MoneyUtils.yuan2Fen(shopPrice) + "");
            }

            String miniProgramPrice = schemeDetail.getMiniProgramPrice();
            fareSchemeDetail.setMiniProgramPrice(miniProgramPrice);
            if (StringUtils.isNotEmpty(miniProgramPrice) && !"0".equals(miniProgramPrice) && !"#".equals(miniProgramPrice)) {
                fareSchemeDetail.setMiniProgramPrice(MoneyUtils.yuan2Fen(miniProgramPrice) + "");
            }

            String douyinPrice = schemeDetail.getDouyinPrice();
            fareSchemeDetail.setDouyinPrice(douyinPrice);
            if (StringUtils.isNotEmpty(douyinPrice) && !"0".equals(douyinPrice) && !"#".equals(douyinPrice)) {
                fareSchemeDetail.setDouyinPrice(MoneyUtils.yuan2Fen(douyinPrice) + "");
            }

            if (org.apache.commons.lang3.StringUtils.isBlank(schemeDetail.getPlatformImageUrl())) {
                return AjaxResult.error("抖音平台上架商品图片地址不可为空！");
            }
            fareSchemeDetail.setPlatformImageUrl(schemeDetail.getPlatformImageUrl());
            fareSchemeDetails.add(fareSchemeDetail);
        }

        slkjFareSchemeDetailMapper.batchInsertSlkjFareSchemeDetail(fareSchemeDetails);

        log.info("新增票价方案 [{}]，同步其他未设置票类。", fareScheme.getId());
        applicationEventPublisher.publishEvent(ActionEvent.of(fareScheme, "新增票价方案！"));
        return AjaxResult.success("添加成功！");
    }

    private boolean isOriginalPriceValid(String shopPrice, String miniProgramPrice, String douyinPrice, String originalPrice) {
        int originalPriceValue = parsePrice(originalPrice);
        if (originalPriceValue == -1) {
            return true;
        }

        int shopPriceValue = parsePrice(shopPrice);
        int miniProgramPriceValue = parsePrice(miniProgramPrice);
        int douyinPriceValue = parsePrice(douyinPrice);


        if ((shopPriceValue != -1 && originalPriceValue <= shopPriceValue) ||
                (miniProgramPriceValue != -1 && originalPriceValue <= miniProgramPriceValue) ||
                (douyinPriceValue != -1 && originalPriceValue <= douyinPriceValue)) {
            return true;
        }
        return false;
    }

    private int parsePrice(String price) {
        if (price == null || price.isEmpty() || price.equals("#") || price.equals("0")) {
            return -1;
        }
        try {
            BigDecimal var1 = new BigDecimal(price);
            BigDecimal var2 = new BigDecimal(100);
            BigDecimal var3 = var1.multiply(var2);
            return Integer.parseInt(var3.stripTrailingZeros().toPlainString());
        } catch (NumberFormatException e) {
            return -1;
        }
    }


    /**
     * 修改票价方案
     *
     * @param request 票价方案
     * @return 结果
     */
    @Override
    public AjaxResult updateSlkjFareScheme(FareSchemeRequest request, Long userId) {
        if (Objects.isNull(request.getId())) {
            return AjaxResult.error("票价方案ID错误！");
        }
        String schemeName = request.getSchemeName();
        if (StringUtils.isEmpty(schemeName)) {
            return AjaxResult.error("票价方案名称不能为空！");
        }
        int count = slkjFareSchemeMapper.countFareSchemeByName(schemeName, request.getId());
        if (count > 0) {
            return AjaxResult.error("票价方案名称已存在！");
        }

        List<FareSchemeRequest.FareSchemeDetailExt> schemeDetails = request.getFareSchemeDetails();
        if (CollectionUtils.isEmpty(schemeDetails)) {
            return AjaxResult.error("票价方案明细不能为空！");
        }
        long counted = schemeDetails.stream().filter(sd -> sd.getChecked() == 1).count();
        if (counted == 0) {
            return AjaxResult.error("票价方案明细至少选择一个！");
        }
        long emptyCount = schemeDetails.stream().filter(sd -> sd.getChecked() == 1 && (StringUtils.isEmpty(sd.getOriginalPrice()) || StringUtils.isEmpty(sd.getShopPrice()) || StringUtils.isEmpty(sd.getDouyinPrice()) || StringUtils.isEmpty(sd.getMiniProgramPrice()))).count();
        if (emptyCount > 0) {
            return AjaxResult.error("价格不能为空！");
        }
        long originalPriceValid = schemeDetails.stream().filter(r -> r.getChecked() == 1 && isOriginalPriceValid(r.getShopPrice(), r.getMiniProgramPrice(), r.getDouyinPrice(), r.getOriginalPrice())).count();
        if (originalPriceValid > 0) {
            return AjaxResult.error("原始价格必须大于实际交易价格！");
        }

        long detailId = schemeDetails.stream().filter(r -> r.getFareSchemeDetailId() == null).count();
        if (detailId > 0) {
            return AjaxResult.error("票价方案明细ID不能为空！");
        }

        SlkjFareScheme fareScheme = new SlkjFareScheme();
        fareScheme.setId(request.getId());
        fareScheme.setSchemeName(schemeName.trim());
        fareScheme.setSchemeRemark(request.getSchemeRemark());
        fareScheme.setUpdateBy(userId + "");
        fareScheme.setUpdateTime(DateUtils.getNowDate());
        slkjFareSchemeMapper.updateSlkjFareScheme(fareScheme);

        List<SlkjFareSchemeDetail> fareSchemeDetails = new ArrayList<>();
        SlkjFareSchemeDetail fareSchemeDetail;
        for (FareSchemeRequest.FareSchemeDetailExt schemeDetail : schemeDetails) {
            fareSchemeDetail = new SlkjFareSchemeDetail();
            fareSchemeDetail.setId(schemeDetail.getFareSchemeDetailId());
            fareSchemeDetail.setChecked(schemeDetail.getChecked());

            String originalPrice = schemeDetail.getOriginalPrice();
            fareSchemeDetail.setOriginalPrice(originalPrice);
            if (StringUtils.isNotEmpty(originalPrice) && !"0".equals(originalPrice) && !"#".equals(originalPrice)) {
                fareSchemeDetail.setOriginalPrice(MoneyUtils.yuan2Fen(originalPrice) + "");
            }

            String shopPrice = schemeDetail.getShopPrice();
            fareSchemeDetail.setShopPrice(shopPrice);
            if (StringUtils.isNotEmpty(shopPrice) && !"0".equals(shopPrice) && !"#".equals(shopPrice)) {
                fareSchemeDetail.setShopPrice(MoneyUtils.yuan2Fen(shopPrice) + "");
            }

            String miniProgramPrice = schemeDetail.getMiniProgramPrice();
            fareSchemeDetail.setMiniProgramPrice(miniProgramPrice);
            if (StringUtils.isNotEmpty(miniProgramPrice) && !"0".equals(miniProgramPrice) && !"#".equals(miniProgramPrice)) {
                fareSchemeDetail.setMiniProgramPrice(MoneyUtils.yuan2Fen(miniProgramPrice) + "");
            }

            String douyinPrice = schemeDetail.getDouyinPrice();
            fareSchemeDetail.setDouyinPrice(douyinPrice);
            if (StringUtils.isNotEmpty(douyinPrice) && !"0".equals(douyinPrice) && !"#".equals(douyinPrice)) {
                fareSchemeDetail.setDouyinPrice(MoneyUtils.yuan2Fen(douyinPrice) + "");
            }

            String platformImageUrl = schemeDetail.getPlatformImageUrl();
            if (org.apache.commons.lang3.StringUtils.isBlank(platformImageUrl)) {
                return AjaxResult.error("抖音平台上架商品图片地址不可为空！");
            }
            fareSchemeDetail.setPlatformImageUrl(platformImageUrl);
            fareSchemeDetails.add(fareSchemeDetail);
        }

        int c = slkjFareSchemeDetailMapper.batchUpdateSlkjFareSchemeDetail(fareSchemeDetails);

        // 将未勾选的对应原始值全部置为 null
        List<Long> detailIds = schemeDetails.stream().map(FareSchemeRequest.FareSchemeDetailExt::getFareSchemeDetailId).collect(Collectors.toList());
        slkjFareSchemeDetailMapper.resetDataToNull(detailIds, request.getId());
        return AjaxResult.success("更新成功！", c);
    }

    /**
     * 批量删除票价方案
     *
     * @param ids 需要删除的票价方案主键
     * @return 结果
     */
    @Transactional
    @Override
    public AjaxResult deleteSlkjFareSchemeByIds(Long[] ids, Long userId) {
        int count = slkjFareSchemeShopMapper.countFareSchemeShopByFareSchemeIds(ids);
        if (count > 0) {
            return AjaxResult.error("票价方案已经门店使用，禁止删除！");
        }

        // 删除主表
        int total = slkjFareSchemeMapper.updateSlkjFareSchemeByIds(ids, userId);
        // 删除子表
        slkjFareSchemeDetailMapper.updateSlkjFareSchemeDetailByFareSchemeIds(ids);
        return AjaxResult.success("删除成功！", total);
    }

    /**
     * 删除票价方案信息
     *
     * @param id 票价方案主键
     * @return 结果
     */
    @Override
    public int deleteSlkjFareSchemeById(Long id) {
        return slkjFareSchemeMapper.deleteSlkjFareSchemeById(id);
    }

    @Override
    public int existsTicketLevelAndTicketCategory() {
        List<SlkjTicketLevel> ticketLevels = slkjTicketLevelMapper.selectSlkjTicketLevelList(null);
        if (CollectionUtils.isEmpty(ticketLevels)) {
            return HttpStatus.TICKET_LEVEL_NOT_EXIST;
        }

        List<SlkjTicketCategory> ticketCategories = slkjTicketCategoryMapper.selectSlkjTicketCategoryList(null);
        if (CollectionUtils.isEmpty(ticketCategories)) {
            return HttpStatus.TICKET_CATEGORY_NOT_EXIST;
        }

        return HttpStatus.SUCCESS;
    }

    @Override
    public AjaxResult getTicketLevelCategory() {
        List<SlkjTicketCategory> ticketCategories = slkjTicketCategoryMapper.selectSlkjTicketCategoryList(null);
        if (CollectionUtils.isEmpty(ticketCategories)) {
            return AjaxResult.error(HttpStatus.TICKET_CATEGORY_NOT_EXIST, "需要先创建票档/票类，才可以做票价方案！");
        }

        final List<FareSchemeTicketLevelCategoryVo> result = new ArrayList<>();

        List<Long> ticketLevelIds = ticketCategories.stream().map(SlkjTicketCategory::getTicketLevelId).collect(Collectors.toList());
        List<SlkjTicketLevel> ticketLevels = slkjTicketLevelMapper.selectSlkjTicketLevelListByIds(ticketLevelIds);
        Map<Long, SlkjTicketLevel> ticketLevelMap = ticketLevels.stream().collect(Collectors.toMap(SlkjTicketLevel::getId, tl -> tl));
        ticketCategories.stream()
                .collect(Collectors.groupingBy(SlkjTicketCategory::getTicketLevelId))
                .forEach((k, v) -> {
                    SlkjTicketLevel ticketLevel = ticketLevelMap.get(k);
                    FareSchemeTicketLevelCategoryVo fstlc = new FareSchemeTicketLevelCategoryVo();
                    fstlc.setTicketLevelNumber(ticketLevel.getNumber());
                    fstlc.setTicketLevelId(ticketLevel.getId());
                    fstlc.setTicketLevelName(ticketLevel.getName());
                    List<FareSchemeTicketLevelCategoryVo.TicketLevelCategory> levelCategories = new ArrayList<>();
                    v.forEach(ticketCategory -> {
                        FareSchemeTicketLevelCategoryVo.TicketLevelCategory ticketLevelCategory = new FareSchemeTicketLevelCategoryVo.TicketLevelCategory();
                        ticketLevelCategory.setTicketCategoryId(ticketCategory.getId());
                        ticketLevelCategory.setTicketCategoryName(ticketCategory.getName());
                        ticketLevelCategory.setTicketCategoryNumber(ticketCategory.getNumber());
                        ticketLevelCategory.setShopPrice("");
                        ticketLevelCategory.setMiniProgramPrice("");
                        ticketLevelCategory.setDouyinPrice("");
                        ticketLevelCategory.setOriginalPrice("");
                        ticketLevelCategory.setChecked((byte) 0);
                        ticketLevelCategory.setFareSchemeDetailId(null);
                        ticketLevelCategory.setTicketLevelId(ticketCategory.getTicketLevelId());
                        levelCategories.add(ticketLevelCategory);
                    });
                    fstlc.setLevelCategories(levelCategories);
                    result.add(fstlc);
                });
        log.info("新建票价方案，页面获取票档和票类的数据 result:{}", JSON.toJSONString(result));
        return AjaxResult.success(result);
    }

    @Override
    public AjaxResult shopSaveFareScheme(SlkjFareSchemeShop fareSchemeShop) {
        slkjFareSchemeShopMapper.updateSlkjFareSchemeShopByShopId(fareSchemeShop.getShopId());

        fareSchemeShop.setId(null);
        fareSchemeShop.setStatus(0);
        slkjFareSchemeShopMapper.insertSlkjFareSchemeShop(fareSchemeShop);

        return AjaxResult.success();
    }

    @Override
    public AjaxResult schemeSelected() {
        SlkjFareScheme fsq = new SlkjFareScheme();
        fsq.setStatus(0);
        List<SlkjFareScheme> fareSchemes = slkjFareSchemeMapper.selectSlkjFareSchemeList(fsq);
        return AjaxResult.success(fareSchemes);
    }

    @Override
    public List<SlkjFareSchemeDetail> selectSlkjFareSchemeDetailByIds(List<Long> ids) {
        return slkjFareSchemeDetailMapper.selectSlkjFareSchemeDetailByIds(ids);
    }

    @Override
    public List<SlkjFareScheme> selectSlkjFareSchemeByIds(List<Long> fareIds) {
        return slkjFareSchemeMapper.selectSlkjFareSchemeByIds(fareIds);
    }

    @Override
    public AjaxResult projects(Long shopId, int source) {
        log.info("获取门店 [{}] 当前正在上映的项目门店价格信息, source:{}！", shopId, source);
        Stopwatch stopwatch = Stopwatch.createStarted();
        SlkjShopProject shopProject = new SlkjShopProject();
        shopProject.setShopId(shopId);
        shopProject.setDelFlag((byte) 0);
        List<SlkjShopProject> shopProjects = slkjShopProjectMapper.selectSlkjShopProjectList(shopProject);
        if (CollectionUtils.isEmpty(shopProjects)) {
            log.info("门店 [{}] 当前还未添加项目！", shopId);
            return AjaxResult.error("请先添加项目！");
        }

        final PosProjectVo result = new PosProjectVo();
        result.setProject(projectMapper.selectProjectByProjectId(shopProjects.get(0).getProjectId()));

        Long fareSchemeId = slkjFareSchemeShopMapper.selectFareSchemeIdByShopId(shopId);
        if (Objects.isNull(fareSchemeId)) {
            log.info("门店 [{}] 当前还未添加票价方案！", shopId);
            return AjaxResult.error("请先添加票价方案！");
        }
        SlkjFareScheme fareScheme = slkjFareSchemeMapper.selectSlkjFareSchemeById(fareSchemeId);
        result.setFareSchemeId(fareSchemeId);
        result.setFareSchemeRemark(fareScheme.getSchemeRemark());

        List<SlkjFareSchemeDetail> schemeDetails = slkjFareSchemeDetailMapper.selectSlkjFareSchemeDetailByFareSchemeIds(Collections.singletonList(fareSchemeId))
                .stream()
                .filter(detail -> detail.getStatus() == 0 && detail.getChecked() == 1)
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(schemeDetails)) {
            log.info("当前票价方案 [{}] 状态全部不可用！", fareSchemeId);
            return AjaxResult.error("当前票价方案没有可选项！");
        }

        long counted = schemeDetails.stream().filter(detail -> {
            switch (OrderSource.fromCode(source)) {
                case DOUYIN:
                    return "#".equals(detail.getDouyinPrice());
                case MINI_PROGRAM:
                    return "#".equals(detail.getMiniProgramPrice());
                case SHOP:
                    return "#".equals(detail.getShopPrice());
                default:
                    return false;
            }
        }).count();
        if (counted == schemeDetails.size()) {
            log.info("当前票价方案 [{}] 均不在当前渠道售卖！", fareSchemeId);
            return AjaxResult.error("当前票不在该渠道售卖！");
        }

        log.info("组装数据结构...");
        List<Long> ticketLevelIds = new ArrayList<>();
        List<Long> ticketCategoryIds = new ArrayList<>();
        for (SlkjFareSchemeDetail schemeDetail : schemeDetails) {
            ticketLevelIds.add(schemeDetail.getTicketLevelId());
            ticketCategoryIds.add(schemeDetail.getTicketCategoryId());
        }
        Map<Long, SlkjTicketLevel> levelMap = slkjTicketLevelMapper.selectSlkjTicketLevelListByIds(ticketLevelIds)
                .stream()
                .collect(Collectors.toMap(SlkjTicketLevel::getId, Function.identity()));

        Map<Long, SlkjTicketCategory> categoryMap = slkjTicketCategoryMapper.selectSlkjTicketCategoryByIds(ticketCategoryIds)
                .stream()
                .collect(Collectors.toMap(SlkjTicketCategory::getId, Function.identity()));

        List<PosProjectVo.Ticket> ticketDetails = schemeDetails.stream()
                .filter(detail -> {
                    switch (OrderSource.fromCode(source)) {
                        case DOUYIN:
                            return !"#".equals(detail.getDouyinPrice());
                        case MINI_PROGRAM:
                            return !"#".equals(detail.getMiniProgramPrice());
                        case SHOP:
                            return !"#".equals(detail.getShopPrice());
                        default:
                            return false;
                    }
                })
                .collect(Collectors.groupingBy(SlkjFareSchemeDetail::getTicketLevelId))
                .entrySet()
                .stream()
                .map(entry -> {
                    // 票档
                    PosProjectVo.Ticket ticket = new PosProjectVo.Ticket();
                    ticket.setTicketLevelId(entry.getKey());
                    SlkjTicketLevel ticketLevel = levelMap.get(entry.getKey());
                    ticket.setTicketLevelName(ticketLevel.getName());
                    ticket.setTicketLevelRemark(ticketLevel.getRemark());
                    // 票类
                    List<PosProjectVo.Category> categories = entry.getValue().stream()
                            .map(schemeDetail -> {
                                PosProjectVo.Category category = new PosProjectVo.Category();
                                category.setTicketCategoryId(schemeDetail.getTicketCategoryId());
                                category.setFareSchemeDetailId(schemeDetail.getId());
                                SlkjTicketCategory ticketCategory = categoryMap.get(schemeDetail.getTicketCategoryId());
                                category.setCategoryName(ticketCategory.getName());
                                category.setQuantity(ticketCategory.getQuantity());
                                category.setPlatformImageUrl(schemeDetail.getPlatformImageUrl());
                                category.setShopPrice(schemeDetail.getShopPrice());
                                category.setMiniAppPrice(schemeDetail.getMiniProgramPrice());
                                category.setDouyinPrice(schemeDetail.getDouyinPrice());
                                category.setOriginalPrice(schemeDetail.getOriginalPrice());
                                return category;
                            })
                            .collect(Collectors.toList());

                    ticket.setCategories(categories);
                    return ticket;
                })
                .collect(Collectors.toList());
        result.setTicketDetails(ticketDetails);

        log.info("组装数据结构完成！{}", JSON.toJSONString(result));
        log.info("门店 [{}] 项目及票价信息获取，共耗时：{}", shopId, stopwatch.stop());
        return AjaxResult.success(result);
    }

}
